package com.photoeditor.demo.model.store.module;

import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;

import com.android.util.NativeVideoUtil;
import com.common.base.ComponentContext;
import com.common.base.log.DLog;
import com.common.base.utils.ProcessUtils;
import com.common.base.utils.thread.ThreadPool;
import com.photoeditor.demo.db.DataManager;
import com.photoeditor.demo.db.ExtraBean;
import com.photoeditor.demo.model.preference.SpUtils;
import com.photoeditor.demo.util.FeatureCodeUtil;
import com.photoeditor.demo.util.FileUtil;
import com.photoeditor.demo.util.PluginVersionUtil;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import static com.photoeditor.demo.db.ExtraBean.RES_TYPE_UNINSTALLED;
import static com.photoeditor.demo.util.TimeConstant.ONE_HOUR;

/**
 * Created by mutour on 2017/5/9.
 */

public abstract class BaseOnlineManager {
    protected static final String TAG = BaseOnlineManager.class.getName();
    private static final String LOCAL_STORE_DATA_DIR = "/.store";

    public static final String STORE_FUNC_KEY_FILTER = "filter";
    public static final String STORE_FUNC_KEY_STICKER = "sticker";
    public static final String STORE_FUNC_KEY_ACTION_BANNER = "action banner";

    public static final int STORE_FUNC_TYPE_DEFAULT_BANNER = -1;
    public static final int STORE_FUNC_TYPE_UNKNOWN = 0;
    public static final int STORE_FUNC_TYPE_FILTER = 1;
    public static final int STORE_FUNC_TYPE_STICKER = 3;
    public static final int STORE_FUNC_TYPE_ACTION_BANNER = 7;

    public static final int NORMAL_RESOURCE_FILE = 4008;
    /*public static final int MUSCLE_RESOURCE_FILE = 4009;*/

    private static final String SP_KEY_APP_VERSION = "SP_KEY_APP_VERSION";


    private static final String FILE_NAME_LAST_UPDATE = "lastupdate";

    private Config mConfig;

    private long mStoreDataLastUpdate;
    private long mStoreRequestLastTime;

    protected StoreOnlineBean mStoreRootOnlineBean;
    private String mStoreDir;
    final ReentrantReadWriteLock mRWLock = new ReentrantReadWriteLock();

    private String mServiceUrl;

    private List<StoreOnlineListener> mListenerList;

    private boolean mIsRequesting;
    private boolean mForceRequest;

    public synchronized void addDataListener(StoreOnlineListener listener) {
        if (listener == null) {
            return;
        }
        if (mListenerList == null) {
            mListenerList = new CopyOnWriteArrayList<>();
        }
        mListenerList.add(listener);
    }

    public synchronized void removeListener(StoreOnlineListener listener) {
        if (mListenerList == null || listener == null) {
            return;
        }
        mListenerList.remove(listener);
    }

    protected synchronized void notifyDataListener(final boolean needUpdate) {
        ThreadPool.runUITask(new Runnable() {
            @Override
            public void run() {
                if (mListenerList == null) {
                    return;
                }
                for (StoreOnlineListener listener : mListenerList) {
                    listener.needUpdateData(needUpdate);
                }
            }
        });
    }

    protected BaseOnlineManager() {
        if ((mConfig = getConfig()) == null) {
            throw new NullPointerException("config不能为空");
        }
        File file = new File(ComponentContext.getContext().getFilesDir(), LOCAL_STORE_DATA_DIR);
        if (!file.exists()) {
            try {
                file.mkdirs();
            } catch (SecurityException ex) {
            }
        }
        mStoreDir = file.getAbsolutePath();

        mStoreDataLastUpdate = SpUtils.obtain().getLong(mConfig.getSpKeyLastUpdate(), 0);
        mStoreRequestLastTime = SpUtils.obtain().getLong(mConfig.getSpKeyLastRequestTime(), 0);
//        if (TestUser.isTestUser()) {
//            mServiceUrl = mConfig.getBaseTestRequestUrl();
//        } else {
            mServiceUrl = mConfig.getBaseRequestUrl();
//        }
        ServerUrlUpdater.getInstance().addOnServerUrlChangeListener(new ServerUrlUpdater.OnServerUrlChangeListener() {
            @Override
            public void onServerUrlChange(String newUrl) {
                // 强制刷新数据
                mStoreRequestLastTime = 0;
                mForceRequest = true;
//                if (TestUser.isTestUser()) {
//                    mServiceUrl = mConfig.getBaseTestRequestUrl();
//                } else {
                    mServiceUrl = mConfig.getBaseRequestUrl();
//                }
                loadData();
            }
        });
    }

    protected abstract Config getConfig();

    public boolean needRequest() {
        if (mForceRequest) {
            mForceRequest = false;
            return true;
        }
        if (mIsRequesting) {
            return false;
        }
        // 只有主进程才请求数据，其它进程读已有的本地数据即可
        if (!ProcessUtils.isMainProcess(ComponentContext.getContext())) {
            return false;
        }
        try {
            PackageManager pm = ComponentContext.getContext().getPackageManager();
            PackageInfo pi = pm.getPackageInfo(ComponentContext.getPackageName(), 0);
            int versionCode = pi.versionCode;
            int spVersionCode = SpUtils.obtain().getInt(SP_KEY_APP_VERSION);
            if (versionCode != spVersionCode) {
                SpUtils.obtain().save(SP_KEY_APP_VERSION, versionCode);
                return true;
            }
        } catch (Exception ignore) {
        }

        long currentTimeMillis = System.currentTimeMillis();
        if (currentTimeMillis - mStoreRequestLastTime > mConfig.getRequestInterval()) {
            return true;
        }
        return false;
    }


    public void loadData() {
        if (needRequest()) {
            mIsRequesting = true;
            // 先请求服务器lastUpdate，判断是否有更新
            String requestLastUpdateUrl = FeatureCodeUtil.addFeatureString(StoreParseUtil.joinUrl(mServiceUrl, FILE_NAME_LAST_UPDATE));
            final String requestUrl = FeatureCodeUtil.addFeatureString(StoreParseUtil.joinUrl(mServiceUrl, mConfig.getRequestFileName()));

            final StoreNetUtil.IRequestListener storeDataListener = new StoreNetUtil.IRequestListener() {
                @Override
                public void onRequestFinish(String text) {
                    if (TextUtils.isEmpty(text)) {
                        return;
                    }
                    // 先解密
                    String dstDecodeStr = NativeVideoUtil.decodeStr(text);
                    text = StoreParseUtil.decompress(dstDecodeStr);
                    if (TextUtils.isEmpty(text)) {
                        return;
                    }
                    boolean isParseSuccess = parse(text);
                    if (!isParseSuccess) {
                        return;
                    }
                    DLog.v(BaseOnlineManager.TAG, text);
                    boolean isSaveSuccess = saveLocalData(text);

                    if (isSaveSuccess) {
                        mStoreRequestLastTime = System.currentTimeMillis();
                        SpUtils.obtain().save(mConfig.getSpKeyLastUpdate(), mStoreDataLastUpdate);
                        SpUtils.obtain().save(mConfig.getSpKeyLastRequestTime(), mStoreRequestLastTime);
                    }
                    notifyDataListener(true);
                    mIsRequesting = false;
                }

                @Override
                public void onRequestFailure() {
                    notifyDataListener(false);
                    mIsRequesting = false;
                }
            };

            StoreNetUtil.IRequestListener lastUpdateListener = new StoreNetUtil.IRequestListener() {
                @Override
                public void onRequestFinish(String text) {
                    long lastUpdateTimeMillis = 0;
                    try {
                        lastUpdateTimeMillis = Long.parseLong(text);
                    } catch (NumberFormatException ex) {
                    }
                    // 如果需要更新
                    if (lastUpdateTimeMillis >= mStoreDataLastUpdate) {
                        StoreNetUtil.getInstance().requestNetworkData(requestUrl, storeDataListener);
                    } else {
                        notifyDataListener(false);
                    }
                    mStoreDataLastUpdate = lastUpdateTimeMillis;
                    mIsRequesting = false;
                }

                @Override
                public void onRequestFailure() {
                    notifyDataListener(false);
                    mIsRequesting = false;
                }
            };
            StoreNetUtil.getInstance().requestNetworkData(requestLastUpdateUrl, lastUpdateListener);
            // 同时填充本地数据
            loadLocalData();
        } else if (!ProcessUtils.isMainProcess(ComponentContext.getContext())
                || mStoreRootOnlineBean == null) {
            // 同时填充本地数据
            loadLocalData();
            notifyDataListener(false);
        }

    }

    @VisibleForTesting
    public void loadDataTest(final StoreOnlineListener listener) {
        // 先请求服务器lastUpdate，判断是否有更新
        final String rootDir = android.os.Environment.getExternalStorageDirectory() + "/POTDCamera/storeData";
        final String requestLastUpdateUrl = StoreParseUtil.joinUrl(rootDir, FILE_NAME_LAST_UPDATE);
        final String requestUrl = StoreParseUtil.joinUrl(rootDir, mConfig.getRequestFileName());

        ThreadPool.runOnNonUIThread(new Runnable() {
            @Override
            public void run() {
                String storeText = FileUtil.readFileToString(requestUrl);
                parse(storeText);
                saveLocalData(storeText);
                if (listener != null) {
                    listener.needUpdateData(true);
                }
            }
        });
        loadLocalData();
    }

    public StoreOnlineBean getRootBean() {
        return mStoreRootOnlineBean;
    }


    private boolean saveLocalData(String text) {
        mRWLock.writeLock().lock();

        boolean result = false;
        try {
            File file = new File(mStoreDir, mConfig.getLocalStoreJsonName());
            if (!TextUtils.isEmpty(text)) {
                String encryptText = NativeVideoUtil.encodeStr(text);
                result = FileUtil.saveStringToFile(encryptText, file.getAbsolutePath());
            }
        } finally {
            mRWLock.writeLock().unlock();
        }
        return result;
    }

    private void loadLocalData() {
        mRWLock.readLock().lock();

        String text = null;
        try {
            File file = new File(mStoreDir, mConfig.getLocalStoreJsonName());

            text = FileUtil.readFileToString(file.getAbsolutePath());
            if (!TextUtils.isEmpty(text)) {
                text = NativeVideoUtil.decodeStr(text);
            }
            DLog.v(BaseOnlineManager.TAG, text);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mRWLock.readLock().unlock();
        }
        if (!TextUtils.isEmpty(text)) {
            parse(text);
        }
    }

    protected boolean parse(String text) {
        JSONObject storeRootOnlineJson = null;
        try {
            storeRootOnlineJson = new JSONObject(text);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        if (storeRootOnlineJson == null) {
            return false;
        }
        StoreOnlineBean data = StoreOnlineBean.parse(storeRootOnlineJson, null);
        data = verifyDataVersion(data);
        verifyBannerVersion(data);
        mStoreRootOnlineBean = data;
        return true;
    }

    /**
     * 校验数据是否支持显示
     */
    protected StoreOnlineBean verifyDataVersion(StoreOnlineBean data) {
        if (data == null) {
            return data;
        }
        ArrayList<StoreOnlineBean> child = data.getChildModules();
        Iterator<StoreOnlineBean> it = child.iterator();
        while (it.hasNext()) {
            StoreOnlineBean item = it.next();
            if (item.getDisplayVersion() > PluginVersionUtil.getPluginSupportShowVersionCode()) {
                // 大于支持的版本，删除
                it.remove();
            } else if (item.getChildModules() != null) {
                // 递归删除子item
                verifyDataVersion(item);
            }
        }
        return data;
    }

    /**
     * 校验banner是否支持显示
     */
    protected StoreOnlineBean verifyBannerVersion(StoreOnlineBean data) {
        if (data == null || data.getChildModules() == null) {
            return data;
        }
        for (int i = 0; i < data.getChildModules().size(); ++i) {
            StoreOnlineBean tmp = data.getChildModules().get(i);
            if (tmp.getRecommends() != null) {
                ArrayList<StoreOnlineBean> child = tmp.getRecommends();
                Iterator<StoreOnlineBean> it = child.iterator();
                while (it.hasNext()) {
                    StoreOnlineBean item = it.next();
                    if (item.getDisplayVersion() > PluginVersionUtil.getPluginSupportShowVersionCode()) {
                        // 大于支持的版本，删除
                        it.remove();
                    }
                }
            }
        }
        return data;
    }

    /**
     * 通过id检索数据
     *
     * @param moduleId {@link StoreOnlineBean#id}
     * @return
     */
    public StoreOnlineBean getBeanByModuleId(String moduleId) {
        if (mStoreRootOnlineBean == null || moduleId == null) {
            return null;
        }
        if (TextUtils.equals(mStoreRootOnlineBean.getId(), moduleId)) {
            return mStoreRootOnlineBean;
        }
        LinkedList<StoreOnlineBean> queue = new LinkedList<>();
        queue.offer(mStoreRootOnlineBean);
        while (!queue.isEmpty()) {
            StoreOnlineBean currentBean = queue.poll();
            if (TextUtils.equals(currentBean.getId(), moduleId)) {
                return currentBean;
            }
            List<StoreOnlineBean> childBeans = currentBean.getChildModules();
            if (childBeans != null && childBeans.size() > 0) {
                for (StoreOnlineBean child : childBeans) {
                    queue.offer(child);
                }
            }
        }
        return null;
    }

    /**
     * 通过id检索数据层级中的父Bean
     *
     * @param moduleId
     * @return
     */
    public StoreOnlineBean getParentBeanByModuleId(String moduleId) {
        if (mStoreRootOnlineBean == null || moduleId == null) {
            return null;
        }
        LinkedList<StoreOnlineBean> queue = new LinkedList<>();
        queue.offer(mStoreRootOnlineBean);
        while (!queue.isEmpty()) {
            StoreOnlineBean currentBean = queue.poll();
            List<StoreOnlineBean> childBeans = currentBean.getChildModules();
            if (childBeans != null && childBeans.size() > 0) {
                for (StoreOnlineBean child : childBeans) {
                    if (TextUtils.equals(child.getId(), moduleId)) {
                        return currentBean;
                    }
                    queue.offer(child);
                }
            }
        }
        return null;
    }

    /**
     * 获取bean的类型
     *
     * @return {@link this#STORE_FUNC_TYPE_UNKNOWN}
     */
    public int getBeanType(StoreOnlineBean bean) {
        if (bean == null) {
            return STORE_FUNC_TYPE_UNKNOWN;
        }
        if (TextUtils.equals(bean.getName(), STORE_FUNC_KEY_FILTER)) {
            return STORE_FUNC_TYPE_FILTER;
        } else if (TextUtils.equals(bean.getName(), STORE_FUNC_KEY_ACTION_BANNER)) {
            return STORE_FUNC_TYPE_ACTION_BANNER;
        }
        return getBeanType(getParentBeanByModuleId(bean.getId()));
    }

    /**
     * 获取大分类的bean，需要通过这个bean来确定类型，是滤镜还是贴纸
     *
     * @return
     */
    public StoreOnlineBean getTypeBean(StoreOnlineBean bean) {
        if (bean == null) {
            return null;
        }
        if (TextUtils.equals(bean.getName(), STORE_FUNC_KEY_FILTER)) {
            return bean;
        }
        return getTypeBean(getParentBeanByModuleId(bean.getId()));
    }

    public List<StoreOnlineBean> getBeanByType(Integer... type) {
        if (mStoreRootOnlineBean == null || type.length == 0) {
            return null;
        }
        List<StoreOnlineBean> result = new ArrayList<>();
        List<StoreOnlineBean> childBeans = mStoreRootOnlineBean.getChildModules();
        if (childBeans == null) {
            return null;
        }
        List<Integer> typeList = Arrays.asList(type);
        for (StoreOnlineBean storeOnlineBean : childBeans) {
            if (typeList.contains(getBeanType(storeOnlineBean))) {
                result.add(storeOnlineBean);
            }
        }
        return result;
    }

    public boolean isInstalledByPkgName(StoreOnlineBean bean, int type) {
        if (bean == null) {
            return false;
        }
        String downUrl = bean.getDownUrl();
        if (downUrl.contains("filter")) {
            return DataManager.getInstance().isExistFilter(bean.getPkgName(), bean.getCategoryId()) ||
                    DataManager.getInstance().isInstalled(bean.getPkgName());
        }
        return false;
    }

    public static boolean isInstalledByPkgName(StoreOnlineBean bean) {
        if (bean == null) {
            return false;
        }
        return DataManager.getInstance().isInstalled(bean.getPkgName());
//        return DataManager.getInstance().isExistFilter(bean.getPkgName())
//                || DataManager.getInstance().isExistSticker(bean.getPkgName())
//                || DataManager.getInstance().isExistBackground(bean.getName())
//                || DataManager.getInstance().isExistMagazine(bean.getPkgName());
    }

    public static boolean isInstalledByPkgName(String pkgName) {
        if (pkgName == null) {
            return false;
        }
        return DataManager.getInstance().isInstalled(pkgName);
//        return DataManager.getInstance().isExistFilter(pkgName)
//                || DataManager.getInstance().isExistSticker(pkgName)
//                || DataManager.getInstance().isExistBackground(pkgName)
//                || DataManager.getInstance().isExistMagazine(pkgName);
    }

    public static ExtraBean toExtraBean(StoreOnlineBean bean, String filePath) {
        return ExtraBean.create(bean.getName()
                , bean.getPkgName()
                , 0
                , 0
                , bean.isBuy()
                , bean.getVersioncode()
                , RES_TYPE_UNINSTALLED
                , filePath
                , bean.isDownloadOnGP());
    }

    public interface StoreOnlineListener {
        void needUpdateData(boolean needUpdate);
    }

    public static class Config {
        private Config() {
        }

        public String getLocalStoreJsonName() {
            return mLocalStoreJsonName;
        }

        public void setLocalStoreJsonName(String localStoreJsonName) {
            mLocalStoreJsonName = localStoreJsonName;
        }

        public String getBaseRequestUrl() {
            return ServerUrlUpdater.getInstance().getNewestServerUrl() + mBaseRequestUrl;
        }

        public void setBaseRequestUrl(String baseRequestUrl) {
            mBaseRequestUrl = baseRequestUrl;
        }

        public String getBaseTestRequestUrl() {
            return ServerUrlUpdater.getInstance().getNewestServerUrl() + mBaseTestRequestUrl;
        }

        public void setBaseTestRequestUrl(String baseTestRequestUrl) {
            mBaseTestRequestUrl = baseTestRequestUrl;
        }

        public String getRequestFileName() {
            return mRequestFileName;
        }

        public void setRequestFileName(String requestFileName) {
            mRequestFileName = requestFileName;
        }

        public String getSpKeyLastUpdate() {
            return mSpKeyLastUpdate;
        }

        public void setSpKeyLastUpdate(String spKeyLastUpdate) {
            mSpKeyLastUpdate = spKeyLastUpdate;
        }

        public String getSpKeyLastRequestTime() {
            return mSpKeyLastRequestTime;
        }

        public void setSpKeyLastRequestTime(String spKeyLastRequestTime) {
            mSpKeyLastRequestTime = spKeyLastRequestTime;
        }

        public long getRequestInterval() {
            return mRequestInterval;
        }

        public void setRequestInterval(long requestInterval) {
            mRequestInterval = requestInterval;
        }

        public int getMaxErrorCount() {
            return mMaxErrorCount;
        }

        public void setMaxErrorCount(int maxErrorCount) {
            mMaxErrorCount = maxErrorCount;
        }

        public String getSpKeyRequestCount() {
            return mSpKeyRequestCount;
        }

        public void setSpKeyRequestCount(String spKeyRequestCount) {
            mSpKeyRequestCount = spKeyRequestCount;
        }

        private String mLocalStoreJsonName;
        private String mBaseRequestUrl;
        private String mBaseTestRequestUrl;
        private String mRequestFileName;
        private long mRequestInterval;
        private int mMaxErrorCount;
        private String mSpKeyLastUpdate;
        private String mSpKeyLastRequestTime;
        private String mSpKeyRequestCount;

    }

    public static class ConfigBuilder {
        private Config mConfig;

        public ConfigBuilder(String baseRequestUrl, String requestFileName) {
            mConfig = new Config();
            mConfig.setBaseRequestUrl(baseRequestUrl);
            mConfig.setRequestFileName(requestFileName);
        }

        public ConfigBuilder localStoreJsonName(String localCacheFileName) {
            mConfig.setLocalStoreJsonName(localCacheFileName);
            return this;
        }

        public ConfigBuilder baseTestRequestUrl(String baseTestRequestUrl) {
            mConfig.setBaseTestRequestUrl(baseTestRequestUrl);
            return this;
        }

        public ConfigBuilder spKeyLastUpdate(String spKeyLastUpdate) {
            mConfig.setSpKeyLastUpdate(spKeyLastUpdate);
            return this;
        }

        public ConfigBuilder requestInterval(int requestInterval) {
            mConfig.setRequestInterval(requestInterval);
            return this;
        }

        public ConfigBuilder maxErrorCount(int maxErrorCount) {
            mConfig.setMaxErrorCount(maxErrorCount);
            return this;
        }

        public ConfigBuilder spKeyRequestCount(String spKeyRequestCount) {
            mConfig.setSpKeyRequestCount(spKeyRequestCount);
            return this;
        }

        public Config build() {
            if (mConfig.getLocalStoreJsonName() == null) {
                mConfig.setLocalStoreJsonName(mConfig.getRequestFileName());
            }
            if (mConfig.getBaseTestRequestUrl() == null) {
                mConfig.setBaseTestRequestUrl(mConfig.getBaseRequestUrl());
            }
            if (mConfig.getSpKeyLastUpdate() == null) {
                mConfig.setSpKeyLastUpdate("store_last_update_" + mConfig.getRequestFileName());
            }
            if (mConfig.getSpKeyLastRequestTime() == null) {
                mConfig.setSpKeyLastRequestTime("store_request_last_time_" + mConfig.getRequestFileName());
            }
            if (mConfig.getSpKeyRequestCount() == null) {
                mConfig.setSpKeyRequestCount("store_request_count" + mConfig.getRequestFileName());
            }
            if (mConfig.getMaxErrorCount() == 0) {
                mConfig.setMaxErrorCount(3);
            }
            if (mConfig.getRequestInterval() == 0) {
                mConfig.setRequestInterval(4 * ONE_HOUR);
            }
            return mConfig;
        }
    }


}

